package com.my.java8;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FutureExample {

	public static class Shop {

		private String name;

		public Shop(String name) {
			this.name = name;
		}

		public String getName() {
			return this.name;
		}

		public double getPrice(String product) {
			return calculatePrice(product);
		}

		public Future<Double> getPriceAsync(String product) {
			CompletableFuture<Double> fPrice = new CompletableFuture<>();
			new Thread(() -> {
				try {
					double price = calculatePrice(product);
					fPrice.complete(price);
				} catch (Exception e) {
					fPrice.completeExceptionally(e);
				}
			}).start();
			return fPrice;
		}

		public Future<Double> getPriceAsync2(String product) {
			return CompletableFuture.supplyAsync(() -> calculatePrice(product));
		}

		private double calculatePrice(String product) {
			delay();
			Random random = new Random();
			return random.nextDouble() * product.charAt(0) + product.charAt(1);
		}

		public static void delay() {
			try {
				Thread.sleep(1000L);
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}
	}

	public static Stream<CompletableFuture<String>> findPricesStream(String product) {
		List<Shop> shops = Arrays.asList(new Shop("BestPrice"), new Shop("LetsSaveBig"), new Shop("MyFavoriteShop"),
				new Shop("BuyItAll"));
		Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {

			@Override
			public Thread newThread(Runnable r) {
				Thread t = new Thread(r);
				t.setDaemon(true);
				return t;
			}

		});
		return shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
				.map(future -> future.thenApply(a->a+1)).map(future -> future.thenCompose(
						quote -> CompletableFuture.supplyAsync(() -> quote+"dddd", executor)));
	}

	public static void main(String[] args) {
		/*
		 * Shop shop = new Shop();
		 * 
		 * Future<Double> fPrice = shop.getPriceAsync("test");
		 * 
		 * try { double price = fPrice.get(); System.out.println(price); } catch
		 * (InterruptedException | ExecutionException e) { e.printStackTrace();
		 * }
		 */

		List<Shop> shops = Arrays.asList(new Shop("BestPrice"), new Shop("LetsSaveBig"), new Shop("MyFavoriteShop"),
				new Shop("BuyItAll"));

		shops.parallelStream().map(shop -> String.format("%s price is %.2f", shop.getName(), shop.getPrice("test")))
				.collect(Collectors.toList());

		Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {

			@Override
			public Thread newThread(Runnable r) {
				Thread t = new Thread(r);
				t.setDaemon(true);
				return t;
			}

		});

		/*
		 * List<CompletableFuture<String>> priceFutures =
		 * shops.stream().map(shop -> CompletableFuture.supplyAsync( () ->
		 * String.format("%s price is %.2f",shop.getName(),
		 * shop.getPrice("test")))) .collect(Collectors.toList());
		 */
		List<CompletableFuture<String>> priceFutures = shops.stream()
				.map(shop -> CompletableFuture.supplyAsync(
						() -> String.format("%s price is %.2f", shop.getName(), shop.getPrice("test"), executor)))
				.map(future -> future.thenApply(str -> str + "////"))
				.map(future -> future.thenCompose(str -> CompletableFuture.supplyAsync(() -> "....", executor)))
				.collect(Collectors.toList());
		priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());

		Future<Double> result = CompletableFuture.supplyAsync(() -> 222.22)
				.thenCombine(CompletableFuture.supplyAsync(() -> 222.44), (a, b) -> a + b);
		try {
			System.out.println(result.get());
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
		
		CompletableFuture[] futures = findPricesStream("myPhone")
				.map(f -> f.thenAccept(System.out::println))
				.toArray(size -> new CompletableFuture[size]);
		CompletableFuture.allOf(futures).join();
		
		try {
			System.out.println(CompletableFuture.anyOf(futures).get());
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
